﻿using Microsoft.Office.Tools.Ribbon;
using Screna;
using Screna.Audio;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using TigerCapture.Core.Models;
using TigerCapture.Core.Services;
using TigerCapture.Core.Settings;
using TigerCapture.Presentation;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;

namespace TigerCapture
{
    public partial class Ribbon
    {
        public static readonly string OutDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), ".capture");

        private FullScreenItem _videoSource;
        private AudioSource _audioSource;
        private string _currentFileName;
        private RecorderController _controller;
        private Operator _operator;

        private void Ribbon_Load(object sender, RibbonUIEventArgs e)
        {
            _videoSource = new FullScreenItem();

            InitialFormats();

            InitialRange(cbxFrameRate, 1, 30, 10);

            InitialRange(cbxImageQuality, 1, 100, 70);

            InitialAudioSources();

            InitialRange(cbxAudioQuality, 1, 100, 50);

            Globals.ThisAddIn.Application.SlideShowBegin += Application_SlideShowBegin;
            Globals.ThisAddIn.Application.SlideShowEnd += Application_SlideShowEnd;
        }

        private void Application_SlideShowEnd(PowerPoint.Presentation Pres)
        {
            if (_operator != null)
            {
                _operator.Close();

                _operator = null;

                if (_controller.State != RecorderState.NotRecording)
                {
                    _controller.Stop();
                }

                _controller = null;
            }
        }

        private void Application_SlideShowBegin(PowerPoint.SlideShowWindow Wn)
        {
            Wn.View.PointerType = PowerPoint.PpSlideShowPointerType.ppSlideShowPointerPen;

            if (_controller != null)
            {
                _operator = new Operator(_controller);

                _operator.Show();
            }
        }

        private void InitialRange(RibbonComboBox comboBox, int min, int max, int def)
        {
            for (var i = min; i <= max; i++)
            {
                var ddItem = Globals.Factory.GetRibbonFactory().CreateRibbonDropDownItem();

                ddItem.Label = i.ToString();

                comboBox.Items.Add(ddItem);
            }

            comboBox.Text = def.ToString();
        }

        private void InitialAudioSources()
        {
            if (_audioSource == null)
            {
                _audioSource = new BassAudioSource();
            }

            cbxAudioRecordingSources.Items.Clear();

            foreach (var source in _audioSource.AvailableRecordingSources)
            {
                var ddItem = Globals.Factory.GetRibbonFactory().CreateRibbonDropDownItem();

                ddItem.Label = source.Name;
                ddItem.Tag = source;

                cbxAudioRecordingSources.Items.Add(ddItem);
            }

            if (cbxAudioRecordingSources.Items.Count > 0)
            {
                cbxAudioRecordingSources.Text = cbxAudioRecordingSources.Items.First().Label;
            }

            cbxAudioLoopbackSources.Items.Clear();

            foreach (var source in _audioSource.AvailableLoopbackSources)
            {
                var ddItem = Globals.Factory.GetRibbonFactory().CreateRibbonDropDownItem();

                ddItem.Label = source.Name;
                ddItem.Tag = source;

                cbxAudioLoopbackSources.Items.Add(ddItem);
            }

            if (cbxAudioLoopbackSources.Items.Count > 0)
            {
                cbxAudioLoopbackSources.Text = cbxAudioLoopbackSources.Items.First().Label;
            }
        }

        private void InitialFormats()
        {
            foreach (var item in FFMpegItem.Items)
            {
                var ddItem = Globals.Factory.GetRibbonFactory().CreateRibbonDropDownItem();

                ddItem.Label = item.ToString();
                ddItem.Tag = item;

                cbxFormat.Items.Add(ddItem);
            }

            if (cbxFormat.Items.Count > 0)
            {
                cbxFormat.Text = cbxFormat.Items.First().Label;
            }
        }

        private void btnReload_Click(object sender, RibbonControlEventArgs e)
        {
            if (_audioSource != null)
            {
                _audioSource.Refresh();

                InitialAudioSources();
            }
        }

        private IImageProvider GetImageProvider()
        {
            Func<Point, Point> transform = P => P;

            var imageProvider = _videoSource.GetImageProvider(cbIncludeCursor.Checked, out transform);

            if (imageProvider == null)
                return null;

            var overlays = new List<IOverlay>();

            if (cbIncludeMouseClicks.Checked || cbIncludeKeystrokes.Checked)
            {
                var mouseClickSettings = new MouseClickSettings() { Display = cbIncludeMouseClicks.Checked };
                var keystrokesSettings = new KeystrokesSettings() { Display = cbIncludeKeystrokes.Checked };

                overlays.Add(new MouseKeyHook(mouseClickSettings, keystrokesSettings));
            }

            return new OverlayedImageProvider(imageProvider, transform, overlays.ToArray());
        }

        private void CheckOutDirectory()
        {
            if (!Directory.Exists(OutDirectory))
            {
                Directory.CreateDirectory(OutDirectory);

                File.SetAttributes(OutDirectory, FileAttributes.Hidden);
            }
        }

        private void btnBegin_Click(object sender, RibbonControlEventArgs e)
        {
            try
            {
                CheckOutDirectory();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), ex.Message);

                return;
            }

            var selectedFFMpegItem = cbxFormat.Items.First(i => i.ToString() == cbxFormat.Text).Tag as FFMpegItem;

            _currentFileName = Path.Combine(OutDirectory, $"{DateTime.Now:yyyy-MM-dd-HH-mm-ss}{selectedFFMpegItem.Extension}");

            IImageProvider imgProvider;

            try
            {
                imgProvider = GetImageProvider();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), ex.Message);

                return;
            }

            var selectedAudioRecordingItem = cbxAudioRecordingSources.Items.First(i => i.Label == cbxAudioRecordingSources.Text).Tag as IAudioItem;
            var selectedAudioLoopbackItem = cbxAudioLoopbackSources.Items.First(i => i.Label == cbxAudioLoopbackSources.Text).Tag as IAudioItem;

            _audioSource.SelectedRecordingSource = selectedAudioRecordingItem;
            _audioSource.SelectedLoopbackSource = selectedAudioLoopbackItem;

            IAudioProvider audioProvider;

            try
            {
                audioProvider = _audioSource.GetAudioProvider();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), ex.Message);

                imgProvider?.Dispose();

                return;
            }

            IVideoFileWriter videoEncoder;

            try
            {
                videoEncoder = selectedFFMpegItem.GetVideoFileWriter(new VideoWriterArgs
                {
                    FileName = _currentFileName,
                    FrameRate = int.Parse(cbxFrameRate.Text),
                    VideoQuality = int.Parse(cbxImageQuality.Text),
                    ImageProvider = imgProvider,
                    AudioQuality = int.Parse(cbxAudioQuality.Text),
                    AudioProvider = audioProvider
                });
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), ex.Message);

                imgProvider?.Dispose();
                audioProvider?.Dispose();

                return;
            }

            var recorder = new Recorder(videoEncoder, imgProvider, int.Parse(cbxFrameRate.Text), audioProvider);

            recorder.ErrorOccured += exception =>
            {
                MessageBox.Show(exception.ToString(), exception.Message);
            };

            _controller = new RecorderController(recorder);

            Globals.ThisAddIn.Application.CommandBars.ExecuteMso("SlideShowFromBeginning");
        }

        private void btnPlay_Click(object sender, RibbonControlEventArgs e)
        {
            ServiceProvider.LaunchFile(new ProcessStartInfo(_currentFileName));
        }

        private void btnUpload_Click(object sender, RibbonControlEventArgs e)
        {
            new Uploader().Show();
        }

        private void btnHelp_Click(object sender, RibbonControlEventArgs e)
        {

        }

        private void btnAbout_Click(object sender, RibbonControlEventArgs e)
        {

        }
    }

    public class RecorderController
    {
        private IRecorder _recorder;

        public RecorderState State { get; private set; }

        public RecorderController(IRecorder recorder)
        {
            _recorder = recorder;

            State = RecorderState.NotRecording;
        }

        public void Start()
        {
            State = RecorderState.Recording;

            _recorder.Start();
        }

        public void Pause()
        {
            if (State == RecorderState.Paused)
            {
                _recorder.Start();

                State = RecorderState.Recording;
            }
            else
            {
                _recorder.Stop();

                State = RecorderState.Paused;
            }
        }

        public async void Stop()
        {
            await StopRecording();
        }

        private async Task StopRecording()
        {
            var rec = _recorder;

            var task = Task.Run(() => rec.Dispose());

            State = RecorderState.NotRecording;

            _recorder = null;

            try
            {
                // Ensure saved
                await task;
            }
            catch (Exception e)
            {
                MessageBox.Show($"Error occured when stopping recording.\nThis might sometimes occur if you stop recording just as soon as you start it.\n\n{e}");
            }
        }
    }
}
